package com.hjjt.filelib

import android.R.attr.src
import android.util.Log
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException
import java.nio.channels.FileChannel


/**
 *
 * @ProjectName:    PluginDemo
 * @Package:        com.hjjt.filelib
 * @ClassName:      FileUtil
 * @Description:
 * @Author:         孙浩
 * @CreateDate:     2022/6/13 11:44
 */
object FileUtil {

    val TAG = "FileUtil"

    /**
     * 文件夹复制.
     *
     * @param curDir 旧文件夹
     * @param newDir 新文件夹
     * @param callBack 回调
     */
    fun copyAllDir(
        curDir: String,
        newDir: String,
        callBack: (copyAllSuccess: Boolean, failedPathList: MutableList<String>, successPathList: MutableList<String>) -> Unit
    ) {
        val curDirFile = File(curDir)
        if (!curDirFile.exists() || curDirFile.isFile) { //文件架不存在或穿错文件夹格式
            callBack.invoke(false, mutableListOf(), mutableListOf())
        } else {
            val fileList = curDirFile.listFiles()?.map {
                it.absolutePath
            }?.toList() //获取文件列表
            if (null == fileList) { //如果文件列表为空 直接返回错误
                callBack.invoke(false, mutableListOf(), mutableListOf())
            } else { //复制文件夹
                copyAllFile(fileList, newDir, callBack)
            }
        }
    }

    /**
     * 多文件复制.
     *
     * @param curPathList 当前文件列表
     * @param newPathList 新文件列表
     */
    fun copyAllFile(
        curPathList: List<String>,
        dirPath: String,
        callBack: (copyAllSuccess: Boolean, failedPathList: MutableList<String>, successPathList: MutableList<String>) -> Unit
    ) {
        val fileList = curPathList.map { File(it) }.toList() //路径转文件
        val dirFile = File(dirPath) //文件夹
        if (!dirFile.exists()) dirFile.mkdirs() //如果文件夹不存在复制文件
        var copyAllSuccess = true //复制成功
        val failedPathList = mutableListOf<String>()
        val successPathList = mutableListOf<String>()
        GlobalScope.launch(Dispatchers.IO) {
            async {
                //轮询文件
                fileList.forEach { curFile ->
                    val targetFile = File(dirPath + File.separator + curFile.name)
                    if (!targetFile.exists()) {
                        async { //多文件同步复制
                            val copyTime = System.currentTimeMillis()
                            Log.e(TAG, "copyAllFile: 复制文件${curFile.absolutePath}")
                            val success = copyPathCheck(
                                curFile,
                                targetFile,
                                failedPathList,
                                successPathList
                            )
                            val completeTime = System.currentTimeMillis()
                            Log.e(
                                TAG,
                                "copyAllFile: 复制文件${curFile.absolutePath}用时，copyTime:${copyTime},completeTime:${completeTime},${completeTime - copyTime}毫秒",
                            )
                            if (!success) {
                                copyAllSuccess = success
                            }
                        }
                    } else { //如果文件存在
                        val curMd5 = MD5Util.checkMD5(curFile.absolutePath)
                        val targetFileMD5 = MD5Util.checkMD5(targetFile.absolutePath)
                        if (curMd5 == targetFileMD5) { //二者文件MD5值相等
                            successPathList.add(curFile.absolutePath) //直接算是成功
                        } else { //二者文件MD5值不相等，证明目标目录存在同名，但不是同个文件
                            val deleteSuccess = targetFile.delete()
                            if (deleteSuccess) { //删除成功
                                val success = copyPathCheck(
                                    curFile,
                                    targetFile,
                                    failedPathList,
                                    successPathList
                                )
                                if (!success) {
                                    copyAllSuccess = success
                                }
                            } else { //删除失败，证明复制失败
                                copyAllSuccess = false //全部成功为否
                                failedPathList.add(curFile.absolutePath) //错误列表添加数据
                            }
                        }
                    }
                }
            }.await() //复制文件
            launch(Dispatchers.Main) {
                callBack.invoke(copyAllSuccess, failedPathList, successPathList) //主线程回调
            }
        }
    }

    private fun copyPathCheck(
        curFile: File,
        targetFile: File,
        failedPathList: MutableList<String>,
        successPathList: MutableList<String>
    ): Boolean {
        val success = copyFileByChannel(curFile.absolutePath, targetFile.absolutePath)
        if (!success) { //复制失败
            failedPathList.add(curFile.absolutePath) //存储错误信息
        } else { //复制成功
            successPathList.add(curFile.absolutePath) //成功列表添加
        }
        return success
    }

    /**
     * 复制文件到新的地址.
     *
     * @param curPath 当前路径
     * @param newPath 新路径
     * @param callBack 成功回调
     */
    fun copyFileToNew(curPath: String, newPath: String, callBack: (complete: Boolean) -> Unit) {
        GlobalScope.launch(Dispatchers.IO) {
            val copyComplete = copyFileByChannel(curPath, newPath)
            launch(Dispatchers.Main) {
                callBack.invoke(copyComplete)
            }
        }
    }


    /**
     * 私有复制文件方法.
     *
     * @param curPath 当前文件路径
     * @param newPath 新文件路径
     * @return
     */
    private fun pCopyFile(curPath: String, newPath: String): Boolean {
        return kotlin.runCatching {
            val curFile = File(curPath)
            // 文件不存在 不是文件 文件不可读直接返回
            if (!curFile.exists()) return false
            if (!curFile.isFile) return false
            if (!curFile.canRead()) return false
            var fIPS = FileInputStream(curPath)
            var fOPS = FileOutputStream(newPath)
            val buffer = ByteArray(1024)
            var byteRead: Int? = null
            while (-1 != (fIPS.read(buffer).also { byteRead = it })) {
                byteRead?.run {
                    fOPS.write(buffer, 0, this)
                }
            }
            return true
        }.getOrElse {
            return false
        }
    }


    /**
     * 加速复制速度
     *
     * @param curPath 当前路径
     * @param targetPath 新路径
     * @return 复制是否成功
     */
    private fun copyFileByChannel(curPath:String,targetPath:String):Boolean{
        val src = File(curPath)
        if (src == null || targetPath == null) {
            return false
        }
        val dest: File = File(targetPath)
        if (dest.exists()) {
            val isSuccess = dest.delete()
            if (!isSuccess) {
                return false
            }
        }
        try {
            val isSuccess = dest.createNewFile()
            if (!isSuccess) {
                return false
            }
        } catch (e: IOException) {
            e.printStackTrace()
            return false
        }

        var result = false
        var srcChannel: FileChannel? = null
        var dstChannel: FileChannel? = null
        try {
            srcChannel = FileInputStream(src).getChannel()
            dstChannel = FileOutputStream(dest).channel
            srcChannel.transferTo(0, srcChannel.size(), dstChannel)
            result = true
        } catch (e: IOException) {
            e.printStackTrace()
        } finally {
            try {
                if (srcChannel != null) {
                    srcChannel.close()
                }
                if (dstChannel != null) {
                    dstChannel.close()
                }
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
        return result
    }

}